package service

import (
	"context"
	baseStore "gitee.com/zaiqiang231/go-base-app/base_app/store"
	"gitee.com/zaiqiang231/zcamp-service-camp/app/model"
	"gitee.com/zaiqiang231/zcamp-service-camp/app/model/req"
	remoteRpc "gitee.com/zaiqiang231/zcamp-service-camp/app/rpc/remote"
	"gitee.com/zaiqiang231/zcamp-service-camp/app/util"
	userProto "gitee.com/zaiqiang231/zcamp-service-user/app/rpc/proto"
	"gorm.io/gorm"
	"strconv"
)

type ServiceComment struct{}

var ServiceCommentInstance = new(ServiceComment)

const (
	CommentToPostType  = 1 //评论
	ReplyToCommentType = 2 //回复评论
	ReplyToReplyType   = 3 //回复回复
)

func (service *ServiceComment) CommentCreate(ctx context.Context, request req.CreateCommentRequest, uid int64) (data model.CommentDetails, err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		_, tmperr := ServicePostInstance.GetPostDetail(req.PostDetailReq{
			PostId:    request.PostId,
			CreateUid: strconv.FormatUint(request.PostUID, 10),
		})
		if tmperr != nil {
			err = tmperr
			return
		}

		uniqueId, tmperr := util.GetCommentUniqueId(ctx, uint64(uid))
		if tmperr != nil {
			err = tmperr
			return
		}
		postID, tmperr := strconv.ParseInt(request.PostId, 10, 64)
		if tmperr != nil {
			err = tmperr
			return
		}
		comment := model.PostComment{
			CID:            uniqueId,
			UID:            uint64(uid),
			PostID:         uint64(postID),
			PostUID:        request.PostUID,
			Content:        request.Content,
			CommentType:    CommentToPostType,
			ReplyUid:       0,
			ReplyCommentId: 0,
			FatherCid:      0,
			Status:         0,
		}
		tmperr = db.Create(&comment).Error
		if tmperr != nil {
			err = tmperr
			return
		}

		userResponse, temperr := remoteRpc.GetUserRpc().UserRpcServiceClient.GetUserInfo(ctx, &userProto.UserInfoRequest{
			Uid: uid,
		})
		if temperr != nil {
			err = temperr
			return
		}
		data = model.CommentDetails{
			CommentId:  strconv.FormatUint(comment.CID, 10),
			CommentUid: comment.UID,
			PostID:     strconv.FormatUint(comment.PostID, 10),
			PostUID:    comment.PostUID,
			Content:    comment.Content,
			CTime:      comment.CreatedAt.Unix(),
			UserInfo: model.UserInfo{
				Uid:       userResponse.Uid,
				NickName:  userResponse.NickName,
				HeaderImg: userResponse.HeaderImg,
			},
		}
	})
	return
}

func (service *ServiceComment) CommentReply(ctx context.Context, request req.CommentReplyRequest, uid int64) (data model.CommentDetails, err error) {
	var tmperr error
	uniqueId, tmperr := util.GetCommentReplyUniqueId(ctx, uint64(uid))
	if tmperr != nil {
		err = tmperr
		return
	}
	postID, tmperr := strconv.ParseInt(request.PostId, 10, 64)
	if tmperr != nil {
		err = tmperr
		return
	}

	commentID, tmperr := strconv.ParseInt(request.CommentID, 10, 64)
	if tmperr != nil {
		err = tmperr
		return
	}
	replyUid := int64(request.ReplyUid)

	comment := model.PostComment{
		CID:            uint64(commentID),
		UID:            uint64(uid),
		PostID:         uint64(postID),
		PostUID:        request.PostUid,
		Content:        request.Content,
		ReplyUid:       request.ReplyUid,
		ReplyCommentId: uniqueId,
	}

	comment.CommentType = ReplyToCommentType
	if request.ReplyCommentFatherId != "" {
		comment.CommentType = ReplyToReplyType
		replyCommentFatherId, tmperr := strconv.ParseInt(request.ReplyCommentFatherId, 10, 64)
		if tmperr != nil {
			err = tmperr
			return
		}
		comment.FatherCid = uint64(replyCommentFatherId)
	}
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		tmperr = db.Create(&comment).Error
	})
	if tmperr != nil {
		err = tmperr
		return
	}

	uids := []int64{uid, replyUid}
	userResponse, temperr := remoteRpc.GetUserRpc().UserRpcServiceClient.GetUserInfoList(ctx, &userProto.UserInfoListRequest{
		Uids: uids,
	})
	if temperr != nil {
		err = temperr
		return
	}
	uidMap := make(map[int64]userProto.UserInfoResponse)
	for _, userInfo := range userResponse.List {
		uidMap[userInfo.Uid] = *userInfo
	}

	data = model.CommentDetails{
		CommentId:  request.CommentID,
		CommentUid: request.ReplyUid,
		PostID:     request.PostId,
		PostUID:    request.PostUid,
		ReplyContent: []model.ReplyContent{
			model.ReplyContent{
				ReplyCommentId:  strconv.FormatUint(comment.ReplyCommentId, 10),
				ReplyMsg:        comment.Content,
				ReplyType:       int(comment.CommentType),
				ReplyCreateTime: comment.CreatedAt.Unix(),
				ReplyInfo: model.UserInfo{
					Uid:       uidMap[uid].Uid,
					NickName:  uidMap[uid].NickName,
					HeaderImg: uidMap[uid].HeaderImg,
				},
				BeRepliedInfo: model.UserInfo{
					Uid:       uidMap[replyUid].Uid,
					NickName:  uidMap[replyUid].NickName,
					HeaderImg: uidMap[replyUid].HeaderImg,
				},
			},
		},
	}

	return data, err
}

func (receiver *ServiceComment) CommentPage(ctx context.Context, request req.CommentPageListRequest) (list []model.CommentDetails, cursor string, hasMore bool, err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		var tempDb = db
		if request.ID != "" {
			tempDb = tempDb.Where("id < ?", request.ID)
		}
		limit := request.Count + 1
		var tmpList []model.PostComment
		err = tempDb.Model(model.PostComment{}).
			Where("post_id = ?", request.PostID).
			Where("post_uid = ?", request.CreateUid).
			Where("type = 1").Order("id desc").Limit(limit).Find(&tmpList).Error

		length := len(tmpList)
		hasMore = length == limit
		if hasMore {
			tmpList = tmpList[:length-1]
		}
		if len(tmpList) > 0 {
			cursor = strconv.Itoa(int(tmpList[len(tmpList)-1].ID))
		}

		uids := make([]int64, 0)
		uidsMap := make(map[int64]int64, 0)
		for _, comment := range tmpList {
			if _, ok := uidsMap[int64(comment.UID)]; !ok {
				uidsMap[int64(comment.UID)] = int64(comment.UID)
				uids = append(uids, int64(comment.UID))
			}
		}
		userResponse, temperr := remoteRpc.GetUserRpc().UserRpcServiceClient.GetUserInfoList(ctx, &userProto.UserInfoListRequest{
			Uids: uids,
		})
		if temperr != nil {
			err = temperr
			return
		}
		uidInfoMap := make(map[int64]userProto.UserInfoResponse)
		for _, userInfo := range userResponse.List {
			uidInfoMap[userInfo.Uid] = *userInfo
		}

		for _, comment := range tmpList {
			item := model.CommentDetails{
				CommentId:  strconv.FormatUint(comment.CID, 10),
				CommentUid: comment.UID,
				PostID:     strconv.FormatUint(comment.PostID, 10),
				PostUID:    comment.PostUID,
				Content:    comment.Content,
				CTime:      comment.CreatedAt.Unix(),
				UserInfo: model.UserInfo{
					Uid:       uidInfoMap[int64(comment.UID)].Uid,
					NickName:  uidInfoMap[int64(comment.UID)].NickName,
					HeaderImg: uidInfoMap[int64(comment.UID)].HeaderImg,
				},
			}
			commentDetails, err := receiver.GetReplyComment(ctx, strconv.FormatUint(comment.CID, 10), 1)
			if err == nil {
				item.ReplyContent = commentDetails.ReplyContent
			}
			item.HasMore = commentDetails.HasMore
			list = append(list, item)
		}

	})
	return
}

func (receiver *ServiceComment) GetReplyComment(ctx context.Context, cid string, page int) (data model.CommentDetails, err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		var tmpList []model.PostComment
		err = db.Model(model.PostComment{}).Where("comment_id = ?", cid).
			Where("type = ? or type = ?", ReplyToCommentType, ReplyToReplyType).
			Order("`id` ASC").
			Offset((page - 1) * 5).Limit(5).
			Find(&tmpList).Error

		uids := make([]int64, 0)
		uidsMap := make(map[int64]int64, 0)
		for _, comment := range tmpList {
			if _, ok := uidsMap[int64(comment.UID)]; !ok {
				uidsMap[int64(comment.UID)] = int64(comment.UID)
				uids = append(uids, int64(comment.UID))
			}
		}

		userResponse, temperr := remoteRpc.GetUserRpc().UserRpcServiceClient.GetUserInfoList(ctx, &userProto.UserInfoListRequest{
			Uids: uids,
		})
		if temperr != nil {
			err = temperr
			return
		}
		uidInfoMap := make(map[int64]userProto.UserInfoResponse)
		for _, userInfo := range userResponse.List {
			uidInfoMap[userInfo.Uid] = *userInfo
		}

		data = model.CommentDetails{
			CommentId: cid,
		}
		if len(tmpList) == 5 {
			data.HasMore = true
		}

		for _, comment := range tmpList {
			data.ReplyContent = append(data.ReplyContent, model.ReplyContent{
				ReplyCommentId:  strconv.FormatUint(comment.ReplyCommentId, 10),
				ReplyMsg:        comment.Content,
				ReplyType:       int(comment.CommentType),
				ReplyCreateTime: comment.CreatedAt.Unix(),
				ReplyInfo: model.UserInfo{
					Uid:       uidInfoMap[int64(comment.UID)].Uid,
					NickName:  uidInfoMap[int64(comment.UID)].NickName,
					HeaderImg: uidInfoMap[int64(comment.UID)].HeaderImg,
				},
				BeRepliedInfo: model.UserInfo{
					Uid:       uidInfoMap[int64(comment.ReplyUid)].Uid,
					NickName:  uidInfoMap[int64(comment.ReplyUid)].NickName,
					HeaderImg: uidInfoMap[int64(comment.ReplyUid)].HeaderImg,
				},
			})
		}

	})
	return data, err
}

func (service *ServiceComment) GetCommentCount(postId string) (count int64, err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		err = db.Model(model.PostComment{}).Where("post_id = ?", postId).Count(&count).Error
	})
	return count, err
}

func (service *ServiceComment) CommentLike(ctx context.Context, uid int64, request req.CommentLikeRequest) (err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		var item model.PostCommentLike
		isDelete := 0
		if request.IsCancel {
			isDelete = 1
		}
		var tempDb = db.Model(model.PostCommentLike{}).Where("comment_id = ?", request.CommentID)
		if request.ReplyCommentId != "" {
			tempDb.Where("reply_comment_id = ?", request.ReplyCommentId)
		}

		commentID, tmperr := strconv.ParseInt(request.CommentID, 10, 64)
		if tmperr != nil {
			err = tmperr
			return
		}
		replyCommentId, tmperr := strconv.ParseInt(request.ReplyCommentId, 10, 64)
		if tmperr != nil {
			err = tmperr
			return
		}

		err = tempDb.
			Assign(model.PostCommentLike{
				CID:            uint64(commentID),
				ReplyCommentId: uint64(replyCommentId),
				CommentType:    request.CType,
				UID:            uint64(uid),
				IsDelete:       uint8(isDelete),
			}).FirstOrCreate(&item).Error
	})
	return err
}

func (service *ServiceComment) CommentDelete(ctx context.Context, request req.CommentDeleteRequest) (err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		var tempDb = db.Model(model.PostComment{}).
			Where("post_id = ?", request.PostId).
			Where("comment_id = ?", request.CommentID)
		if request.ReplyCommentId != "" {
			tempDb.Where("reply_comment_id = ?", request.ReplyCommentId)
		}
		tempDb.Delete(&model.PostComment{})
	})
	return err
}
